package com.github.cyf.databaseLock;

import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.StringUtils;
import java.time.Duration;
import java.util.UUID;
import java.util.concurrent.ScheduledFuture;

/**
 * @author chenyifan
 * @create 2024-07-11 14:56
 */
public abstract class AbstractSimpleLock implements ILock {

    private final ThreadPoolTaskScheduler taskScheduler;

    /**
     * 锁持有时间：30s
     */
    protected static final long LOCK_HOLD_TIME = 30l;

    protected AbstractSimpleLock(ThreadPoolTaskScheduler taskScheduler) {
        this.taskScheduler = taskScheduler;
    }

    @Override
    public Locker acquire(String key) {
        String token = doAcquire(key, UUID.randomUUID().toString());
        if (!StringUtils.hasText(token)) {
            throw new RuntimeException("其他人正在处理中，请稍后重试");
        }
        ScheduledFuture scheduledFuture = scheduleRefreshLock(key, token);
        return new Locker(key, token, scheduledFuture);
    }

    @Override
    public boolean release(Locker locker) {
        cancelSchedule(locker.getScheduledFuture());
        return doRelease(locker.getKey(), locker.getToken());
    }

    /**
     * 定时调度每 10s 进行续签锁
     * @param key
     * @param token
     * @return
     */
    private ScheduledFuture scheduleRefreshLock(String key, String token) {
         return taskScheduler.scheduleAtFixedRate(() -> refreshLock(key, token), Duration.ofSeconds(LOCK_HOLD_TIME / 3));
    }

    /**
     * 取消定时调度
     * @param scheduledFuture
     * @return
     */
    private void cancelSchedule(ScheduledFuture<?> scheduledFuture) {
        // isDone : 表示任务是否已经完成了它的执行周期
        if (scheduledFuture != null && !scheduledFuture.isDone() && !scheduledFuture.isCancelled()) {
            scheduledFuture.cancel(true);
        }
    }

    /**
     * 刷新锁的过期时间
     * @param key
     * @param token
     * @return
     */
    public abstract boolean refreshLock(String key, String token);

    public abstract String doAcquire(String key, String token);

    public abstract boolean doRelease(String key, String token);
}
